using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using TaskPluginInterface;
using Schedule;

namespace ApplicationPlugins
{
    /// <summary>
    /// This plugin will run a command every x number of seconds.
    /// </summary>
    [Plugin(PluginType.Executable)]
    public class RunCommand : ITaskPlugin
    {

        #region Private Variables

        // For configuration
        private RunCommandConfig _config;

        // For Progress
        private Utilities.Progress _progress;

        // Other Variables
        private readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        // Cancel button was clicked
        private bool _cancel;

        #region Private Enums

        #endregion Private Enums

        #region Constants

        #endregion Constants

        #endregion Private Variables

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RunCommand"/> class.
        /// </summary>
        /// <param name="progress">The progress of the command.</param>
        public RunCommand(Utilities.Progress progress)
        {
            loadConfiguration();

            _progress = progress;
        }

        #endregion Constructors

        #region ITaskPlugin Members

        #region ITaskPlugin Properties

        /// <summary>
        /// The name of the plug-in.
        /// </summary>
        /// <value></value>
        public string Name
        {
            get { return Properties.RunCommandPlugin.PluginName; }
        }

        /// <summary>
        /// The description of the plug-in.
        /// </summary>
        /// <value></value>
        public string Description
        {
            get { return Properties.RunCommandPlugin.PluginDescription; }
        }

        /// <summary>
        /// The extension that this plug-in handles.
        /// </summary>
        /// <value></value>
        public string Extension
        {
            get { return Properties.RunCommandPlugin.PluginExtension; }
        }

        /// <summary>
        /// The description of the extension that this plug-in handles.
        /// </summary>
        /// <value></value>
        public string ExtensionDescription
        {
            get { return Properties.RunCommandPlugin.PluginExtensionDescription; }
        }

        /// <summary>
        /// The version of the plug-in.
        /// </summary>
        /// <value></value>
        public string Version
        {
            get { return Properties.RunCommandPlugin.PluginVersion; }
        }

        /// <summary>
        /// The author of the plug-in.
        /// </summary>
        /// <value></value>
        public string Author
        {
            get { return Properties.RunCommandPlugin.PluginAuthor; }
        }

        /// <summary>
        /// Returns the schedule for the plug-in.
        /// </summary>
        /// <value></value>
        public List<IScheduledItem> Schedule
        {
            get; set;
        }

        #endregion ITaskPlugin Properties

        #region ITaskPlugin Methods

        /// <summary>
        /// Executes the plug-in.
        /// </summary>
        /// <returns>A <see cref="ExecuteResult"/></returns>
        public ExecuteResult Execute()
        {

            #region Variables

            #endregion Variables

            #region Data Validation

            #endregion Data Validation

            try
            {
                // Setup the Progress Information
                _progress.TotalTasks = 1;
                _progress.TotalItems = 1;
                _progress.CurrentTask = 0;
                _progress.CurrentItem = 0;
                _cancel = false;

                _log.Debug(Properties.Resources.Execute_Started);

                // If the EventStart event has been subscribed to let's fire it.
                if (EventStart != null)
                    EventStart(this, new PluginEventArgs(_progress, "Starting the task.", null));

                doWork();

                // If the EventStart event has been subscribed to let's fire it.
                if (EventEnd != null)
                {
                    if (_cancel)
                    {
                        EventEnd(this, new PluginEventArgs(_progress, "Task execution was canceled.", null));
                    }
                    else
                    {
                        EventEnd(this, new PluginEventArgs(_progress, "Task execution is complete.", null));
                    }
                }

                

                // Exit 
                if (_cancel)
                {
                    _log.DebugFormat(Properties.Resources.Execute_Ended, ExecuteResult.Canceled);
                    return ExecuteResult.Canceled;
                }
                _log.DebugFormat(Properties.Resources.Execute_Ended, ExecuteResult.Ok);
                return ExecuteResult.Ok;
            }
            catch (Exception ex)
            {
                if (EventExceptionOccurred != null)
                    EventExceptionOccurred(this, new PluginEventArgs(_progress, "", ex));

                // Exit with an Exception
                return ExecuteResult.Exception;
            }

        }

        /// <summary>
        /// Cancels the execution of a plug-in
        /// </summary>
        /// <returns>
        /// True, if the cancel request was excepted, otherwise false.
        /// </returns>
        public bool Cancel()
        {
            _log.Debug(Properties.Resources.CancelMethodExecuted);
            _cancel = true;
            return true;
        }
        #endregion ITaskPlugin Methods

        #region ITaskPlugin Events
        /// <summary>
        /// This is used to notify the plug-in host about the starting of the Execute Method.
        /// </summary>
        public event OnStart EventStart;
        /// <summary>
        /// This is used to notify plug-in host about the ending of the Execute Method.
        /// </summary>
        public event OnEnd EventEnd;
        /// <summary>
        /// This is used to notify plug-in host about the processing of the Execute Method.
        /// </summary>
        public event OnProcessing EventProcessing;
        /// <summary>
        /// This is used to notify plug-in host about any Exceptions that have occurred.
        /// </summary>
        public event OnExceptionOccurred EventExceptionOccurred;
        #endregion ITaskPlugin Events

        #endregion

        #region Private Methods

        /// <summary>
        /// Read the list of commands from the configuration file and execute each of them.
        /// </summary>
        /// <returns>A <see cref="TaskPluginInterface.ExecuteResult"/>, with the result of the method call.</returns>
        private ExecuteResult doWork()
        {

            #region Variables

            Process process = new Process();

            #endregion Variables

            // Setup the Progress Information
            _progress.TotalTasks = 1;
            _progress.TotalItems = _config.CommandsToRun.Count;
            _progress.CurrentTask = 0;
            _progress.CurrentItem = 0;
            _cancel = false;

            _log.DebugFormat(Properties.Resources.CommandsFound, _config.CommandsToRun.Count);

            foreach (ProcessStartInfo psi in _config.CommandsToRun)
            {
                
                process.StartInfo = psi;
                process.Start();

                _log.DebugFormat(Properties.Resources.ExecutingCommand, process.StartInfo.FileName);

                // Update the current item
                _progress.CurrentItem++;
                if (EventProcessing != null)
                {
                    PluginEventArgs evt = new PluginEventArgs(_progress, "Working", null);
                    EventProcessing(this, evt);
                    if (evt.Cancel)
                    {
                        _log.Debug(Properties.Resources.CancelRequestRecieved);
                        _cancel = true;
                        break;
                    }
                }
            }
            
            // Everything Done
            if (_cancel)
            {
                return ExecuteResult.Canceled;
            }
            _progress.CurrentTask++;
            return ExecuteResult.Ok;
        }

        #endregion Private Methods

        #region Helper Functions

        #region loadConfiguration

        private void loadConfiguration()
        {
            string configFile = System.Reflection.Assembly.GetExecutingAssembly().Location + ".Settings.xml";
            _config = new RunCommandConfig(null, configFile);

            if (!string.IsNullOrEmpty(_config.Log4NetConfig))
            {
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(_config.Log4NetConfig);
                log4net.Config.XmlConfigurator.Configure(xml.DocumentElement);
            }

            this.Schedule = ScheduleConfig.Load(_config.Schedule);

            _log.Debug(Properties.Resources.ConfigurationLoaded);
        }
        #endregion loadConfiguration

        #endregion Helper Functions

    }
}
